package org.uc3m.pfc.activity;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;
import org.opencv.samples.tutorial1.R;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.uc3m.pfc.data.Action;
import org.uc3m.pfc.device.DeviceManager;
import org.uc3m.pfc.device.IDeviceManager;
import org.uc3m.pfc.lifecycle.ILifeCycle;
import org.uc3m.pfc.lifecycle.State;
import static org.uc3m.pfc.lifecycle.State.starting;
import static org.uc3m.pfc.lifecycle.State.detecting;
import static org.uc3m.pfc.lifecycle.State.processing;

import org.uc3m.pfc.marker.manager.IMarkerFilter;
import org.uc3m.pfc.marker.manager.IMarkerManager;
import org.uc3m.pfc.marker.manager.MarkerFilter;
import org.uc3m.pfc.marker.manager.MarkerManager;
import org.uc3m.pfc.ui.ActionSelector;
import org.uc3m.pfc.ui.ActionSelectorType;
import org.uc3m.pfc.ui.GuiUtils;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.Spinner;
import android.widget.TextView;


/**
 * 
 * @author esteban
 *
 */
public final class MainActivity extends Activity implements 
	CvCameraViewListener2, ILifeCycle, OnItemSelectedListener {
	
	// Class fields *******************************************************
	
	/** Tag for log. */
    private static final String TAG = "OCVSample::Activity";

    /** Maximum number of seconds. */
	private static final int MAX_TIME_SPINNER = 5;

    /** Camera view instance. */
    private CameraBridgeViewBase mOpenCvCameraView;
    /** Flag for changing camera. */
    private boolean mIsJavaCamera = true;
    
	/** Callback method for opencv. */
	private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
		@Override
		public void onManagerConnected(final int status) {
			switch (status) {
			case LoaderCallbackInterface.SUCCESS:

				Log.i(TAG, "OpenCV loaded successfully");
				mOpenCvCameraView.enableView();
				break;
			default:

				super.onManagerConnected(status);
				break;
			}
		}
	};

    /** Marker manager instance. */
    private IMarkerManager markerManager;
    /** Utility for UI management. */
    private GuiUtils guiUtils; 
    /** User action selector. */
    private ActionSelector actionSelector;
    /** Marker filter. */
    private IMarkerFilter markerFilter;
    /** Device manager. */
    private IDeviceManager deviceManager;
    
    /** List of actions to show the user, null if empty. */
    private List<Action> actions;
    
    /** Current state. */
    private State state;
	
    /** Spinner. */
    private Spinner spinner;
    
    // Constructor *******************************************************
    
    /**
     * Default constructor.
     */
    public MainActivity() {
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    
    // Activity methods **************************************************
    
    /** 
     * Called when the activity is first created.
     * @param savedInstanceState instance
     */
    @Override
    public void onCreate(final Bundle savedInstanceState) {
    	
    	setState(State.starting);
    	
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.tutorial1_surface_view);

    	addItemsOnSpinner();
        
        // java or native camera, java by default
        if (mIsJavaCamera) {
            mOpenCvCameraView = (CameraBridgeViewBase) findViewById(
            		R.id.tutorial1_activity_java_surface_view);
        } else {
            mOpenCvCameraView = (CameraBridgeViewBase) findViewById(
            		R.id.tutorial1_activity_native_surface_view);
        }
        
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);

        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
    	
    	this.selectMenu(menu);

		return super.onPrepareOptionsMenu(menu);
    }

	@Override
    public boolean onCreateOptionsMenu(final Menu menu) {
    		
		MenuInflater menuInflater = getMenuInflater();
		menuInflater.inflate(R.layout.menu, menu);
        
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {

    	this.executeOption(item);
    	
    	return true;
    }    

	@Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null) {
            mOpenCvCameraView.disableView();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_5, this, 
        		mLoaderCallback);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null) {
            mOpenCvCameraView.disableView();
        }
    }

    
    // CvCameraViewListener2 methods *****************************************
    

    @Override
    public void onCameraViewStarted(final int width, final int height) {
    	
    	guiUtils = GuiUtils.getInstance();
    	guiUtils.setActivity(this);
    	
    	markerManager = MarkerManager.getInstance();

    	markerFilter = MarkerFilter.getInstance();
    	
    	actionSelector = ActionSelector.getSelector();

    	deviceManager = DeviceManager.getInstance();
    	
    	setState(detecting);
    }
    
    @Override
    public void onCameraViewStopped() {
    }

    @Override
    public Mat onCameraFrame(final CvCameraViewFrame inputFrame) {
    	
		if (markerManager != null) {

			if (getState() != starting) {

				return markerManager.processFrame(inputFrame.rgba());
			} else {
				return inputFrame.rgba();
			}

		} else {
			return inputFrame.rgba();
		}
    }

    
    // LifeCycle methods ****************************************************
    
	@Override
	public void setState(final State pstate) {
		
		State previousState = this.state;
		this.state = pstate;
		
		switch (state) {
		case detecting:
			
			// from processing to detecting
			if (previousState == processing) {
				this.closeOptionsMenu();
				actions = null;
			}
			
			break;
			
		case processing:
			
			this.openOptionsMenu();
			break;
			
		default:
			break;
		}
		
		
	}

	@Override
	public State getState() {
		return this.state;
	}
	
	@Override
	public void setSubstate(final State substate) {
	}
	
	@Override
	public State getSubstate() {
		return null;
	}

	
    // OnItemSelectedListener methods **************************************
	
	
	@Override
	public void onItemSelected(final AdapterView<?> parent, final View view, 
			final int pos, final long id) {

		String seconds = parent.getItemAtPosition(pos).toString();
		
		long time = Long.parseLong(seconds);
		
		if (markerFilter != null) {
			
			if (getState() != starting) {
				markerFilter.setMaxTime(time);
			}
		}
	}


	@Override
	public void onNothingSelected(final AdapterView<?> arg0) {
		// TODO Auto-generated method stub
		
	}
	
	
    // Public methods *****************************************************
	
	/**
     * Set the current list of actions to show in the menu.
     * 
     * @param pactions list of actions
     */
    public void setActions(final List<Action> pactions) {
		this.actions = pactions;
	}
    
	
    // Private methods ****************************************************
	
    /**
     * Add items into spinner dynamically.
     */
    private void addItemsOnSpinner() {
   
		spinner = (Spinner) findViewById(R.id.spinner2);
		
		List<Integer> list = new ArrayList<Integer>();
		
		for (int i = 1; i <= MAX_TIME_SPINNER; i++) {
			list.add(i);
		}
		
		ArrayAdapter<Integer> dataAdapter = new ArrayAdapter<Integer>(this,
				android.R.layout.simple_spinner_item, list);
		dataAdapter.setDropDownViewResource(
				android.R.layout.simple_spinner_dropdown_item);
		
		spinner.setAdapter(dataAdapter);
		
		spinner.setOnItemSelectedListener(this);
    }
    
    
    /**
     * Select the menu from the state of activity.
     * 
     * @param menu to show
     */
    private void selectMenu(final Menu menu) {

    	
		menu.clear();

		switch (getState()) {
		case detecting:
		case starting:
			
			// normal menu
			menu.add(R.string.txt);
			menu.add(R.string.txt_tts);
			menu.add(R.string.txt_tts_recog);
			menu.add(R.string.txt_recog);
			menu.add(R.string.tts_recog);
			
			menu.add(R.string.time);
			menu.add(R.string.server);
			
			break;
			
		case processing:

			// actions menu
			for (Action action : actions) {
				menu.add(action.getName());
			}
			
			break;

		default:
			break;
		}    	
		
	}

    /**
     * Execute the option selected in the menu.
     * 
     * @param item menu item selected
     */
	private void executeOption(final MenuItem item) {

    	String title = item.getTitle().toString();
    	
    	switch (this.getState()) {
		case detecting:
		case starting:
			
			List<ActionSelectorType> selectorTypes = getActionSelectorTypes(
					title);
			
	    	if (!selectorTypes.isEmpty()) {
	        	
	    		actionSelector.setTypes(selectorTypes);
	    		
	    	} else if (title.contains(getResources().getString(
	    			R.string.time))) {
	    		
	    		int position = ((int) markerFilter.getMaxTime()) - 1;
	    		spinner.setSelection(position);
	    		spinner.performClick();
	    		
	    	}  else if (title.contains(getResources().getString(
	    			R.string.server))) {
	    		
	    		showServerOptions();
	    	}
			
			break;

		case processing:
			
			processAction(title);
			
			break;
			
		default:
			break;
		}
		
	}
    
	private void setServerOptions(EditText ipView, EditText portView) {
				
		String ip = ipView.getText().toString();
		String portAux = portView.getText().toString();
		
		// TODO validate input
		
		int port = Integer.parseInt(portAux);
		
		deviceManager.setIp(ip);
		deviceManager.setPort(port);
		
	}
	
	private void showServerOptions() {
		
		// custom dialog
		final Dialog dialog = new Dialog(this);
		dialog.setContentView(R.layout.second);
		dialog.setTitle(getResources().getString(R.string.server));

		Button dialogButton = (Button) dialog.findViewById(R.id.save_button);
		
		final EditText ipView = (EditText) dialog.findViewById(R.id.ip);
		final EditText portView = (EditText) dialog.findViewById(R.id.port);
		
		ipView.setText(deviceManager.getIp());
		portView.setText(String.valueOf(deviceManager.getPort()));
		
		// if button is clicked, close the custom dialog
		dialogButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(final View v) {
				
				
				setServerOptions(ipView, portView);
				
				dialog.dismiss();
			}
		});

		dialog.show();

	}

	/**
	 * Select an action from the option menu.
	 * 
	 * @param title selected menu item
	 */
	private void processAction(final String title) {

		if (actions != null) {
			
			Action actionFound = null;
			
			for (Action action : actions) {
				
				if (action.getName().equals(title)) {
					
					actionFound = action;
					break;
				}
			}
			if (actionFound != null) {

				actionSelector.notifyUserInputButton(
						actionFound.getName());
				
			} else {
				// TODO handle error
			}
			
		}
		
	}

	/**
	 * Get the action selector types from option selected.
	 * 
	 * @param title of menu item
	 * @return list of action selector types
	 */
    private List<ActionSelectorType> getActionSelectorTypes(
    		final String title) {

    	List<ActionSelectorType> modalTypes = 
    			new ArrayList<ActionSelectorType>();

    	if (title.contains(getResources().getString(R.string.txt))) {
    		modalTypes.add(ActionSelectorType.TXT);
    	}
    	if (title.contains(getResources().getString(R.string.tts))) {
    		modalTypes.add(ActionSelectorType.TTS);
    	}
    	if (title.contains(getResources().getString(R.string.recog))) {
    		modalTypes.add(ActionSelectorType.RECOG);
    	}
    	
    	return modalTypes;
	}
	
}
